# -*- coding: utf-8 -*-
# @Author: wfy
# @Date:   2020-04-10 15:31:44
# @Last Modified by:   wfy
# @Last Modified time: 2020-04-19 10:06:13
import random
import matplotlib.pyplot as plt


class Solution():
    """to achieve SJF"""

    def __init__(self):
        super(Solution, self).__init__()
        # save processes
        self.data = []
        self.name = ''
        self.service_time = 0
        self.arrival_time = 0
        self.priority = 0
        self.state = ''
        self.number = 0
        self.timeout = 0
        self.start = 0
        self.end = 0
        self.n = 1

    def insert_data(self, name, arrival_time, service_time, priority):
        self.data.append({
            'id': self.number,
            'name': name,
            'arrival_time': int(arrival_time),
            'service_time': int(service_time),
            'priority': int(priority),
            'state': 'w',
            'turnaround_time': 0,
            'authorized_turnover_time': 0,
            'start': [],
            'end': [],
        })
        self.timeout = max(self.timeout, int(arrival_time))
        self.number += 1

    def get_data_file(self):
        with open('data2.txt', "r", encoding="utf-8") as file:
            for line in file.read().splitlines():
                name, arrival_time, service_time, priority = line.split()
                # insert the process
                self.insert_data(name, arrival_time, service_time, priority)
        file.close()
        # initial queue
        # sort first arrival_time and second service_time
        self.data.sort(key=lambda x: (x['arrival_time'], x['priority']))
        # update and recode id
        for i in range(self.number):
            self.data[i]['index'] = i

    def get_data_input(self):
        print('How many processes do you want input?')
        processes_number = int(input('Please enter an integer of type int:'))
        print('name and arrival_time and service_time and priority of process')
        print('such as:A 0 5 3')
        for _ in range(processes_number):
            name, arrival_time, service_time, priority = input(
                'Please enter\n').split()
            self.insert_data(name, arrival_time, service_time, priority)
        # initial queue
        # sort first arrival_time and second service_time
        self.data.sort(key=lambda x: (x['arrival_time'], x['priority']))
        # update and recode id
        for i in range(self.number):
            self.data[i]['index'] = i

    def show_data_running(self, start, end, data):
        print("name\tstate\tpriority")
        for process in data:
            print("{:<7}\t{:6}\t{}".format(
                process['name'], process['state'], process['priority']))

    def show_data(self):
        print("{:<6}{:<10}{:<10}{:<10}{:<6}{:<8}{:<7}{:<6}".format(
            'name', 'arr_time', 'ser_time', 'priority', '周转时间', '带权周转时间', 'start', 'end'))
        for process in sorted(self.data, key=lambda x: x['id']):
            print("{:<6}{:<10}{:<10}{:<10}{:<10}{:<14.2f}{}{}".format(
                process['name'],
                process['arrival_time'],
                process['service_time'],
                process['priority'],
                process['turnaround_time'],
                process['authorized_turnover_time'],
                process['start'],
                process['end']))

    def update_information(self, index, start, end, data):
        self.data[index]['state'] = 'R'
        print('-' * 40)
        # print("running order is:", *[i['name'] for i in data], sep='->')
        print("from {:} to {:}".format(start, end))
        print("the running order is :")
        self.show_data_running(start, end, data)
        self.data[index]['start'].append(start)
        self.data[index]['end'].append(end)
        # had finished
        self.data[index]['state'] = 'w'
        if len(self.data[index]['end']) == self.data[index]['service_time']:
            self.data[index]['state'] = 'f'
            self.data[index]['turnaround_time'] = end - \
                                                  self.data[index]['arrival_time']
            self.data[index]['authorized_turnover_time'] = self.data[index]['turnaround_time'] / \
                                                           self.data[index]['service_time']
        else:
            self.data[index]['priority'] -= self.n
        self.start = start
        self.end = end
        # self.show_data()
        print("the finish status is :")
        self.show_data_running(start, end, data)

    def to_get_index(self, data, process):
        for i in range(len(data)):
            if process['priority'] > data[i]['priority']:
                return i
        return len(data)

    def get_next_data(self, index, data):
        # get processes from the beginning to the end of the current process
        result = [x for x in self.data if x['arrival_time'] <=
                  self.end and x['state'] == 'w' and x not in data]
        if result or data:
            # maintain the queue
            for process in result:
                data.insert(self.to_get_index(data, process), process)
            return data
        # no processes entered at current time
        for process in self.data:
            if process['state'] == 'w':
                self.start = self.end = process['arrival_time']
                return [process]
        return []

    def implement(self):
        '''start algorithm'''
        # get first process
        data = [self.data[0]]
        # update the time of start
        self.start = self.end = data[0]['arrival_time']
        while data:
            # update information
            self.update_information(
                data[0]['index'], self.end, self.end + 1, data)
            # get next process or processes
            data = self.get_next_data(data.pop(0)['index'], data)
        self.data.sort(key=lambda x: x['id'])

    def get_y_ticks(self):
        return [x['id'] for x in self.data] + [self.data[-1]['id'] + 1], [x['name'] for x in self.data] + ['']

    def init_image(self):
        # size = 1000 * 500
        plt.figure('SJF', figsize=(10, 5))
        self.drow_image()
        # setting xticks for 0 to self.end + 2
        plt.xticks([i for i in range(self.end + 3)])
        # setting title
        plt.title('the time of process about this')

        plt.xlabel('')
        plt.ylabel('processes')
        # setting yticks.such as A == 0
        plt.yticks(self.get_y_ticks()[0], self.get_y_ticks()[1])

    def drow_image(self):
        colormap = plt.cm.gist_ncar  # nipy_spectral, Set1,Paired
        colors = ['r', 'g', 'b', 'c', 'm', 'y', 'k'] + \
                 [colormap(random.random()) for i in range(len(self.data) + 1)]
        for i in range(len(self.data)):
            # the time line of process from start to end
            process = self.data[i]
            plt.plot([process['start'][0], process['end'][0]],
                     [process['id'], process['id']],
                     label=process['name'],
                     color=colors[i],
                     lw=2)
            for x1, x2 in zip(process['start'][1:], process['end'][1:]):
                plt.plot([x1, x2],
                         [process['id'], process['id']],
                         color=colors[i],
                         lw=2)
            # annotation of the key point
            plt.plot([process['end'][-1], process['end'][-1]],
                     [-1, process['id']],
                     'k--',
                     lw=1)
        # legend
        plt.legend(loc='best')

    def set_ax(self):
        ax = plt.gca()  # 获取到当前坐标轴信息
        ax.spines['right'].set_color('none')
        ax.spines['bottom'].set_color('none')
        ax.xaxis.set_ticks_position('top')  # 将X坐标轴移到上面
        ax.invert_yaxis()  # 反转Y坐标轴
        ax.grid(True, linestyle='-.')  # 网格

    def show_image(self):
        self.init_image()
        self.set_ax()
        plt.savefig('result.png', dpi=300)
        plt.show()

    def main(self):
        self.get_data_file()
        self.show_data()
        self.implement()
        self.show_data()
        self.show_image()


if __name__ == '__main__':
    try:
        Solution().main()
    except Exception as e:
        print('发生异常', e)
    else:
        print("end")
    finally:
        print("finally")